#include "CRadarTest.h"
#include "CMediaFile.h"
#include "GenOpt.h"
#include "Logger.h"
#include "radar_detect.h"
#include <QDateTime>
#include <QDir>
#include <qcontainerfwd.h>
#include <qdatetime.h>
#include <qsurfaceformat.h>

int CThreadRadarTest::OnSocTrig(int soc_state, void *param) {
  CThreadRadarTest *pThis = (CThreadRadarTest *)param;
  if (soc_state == 0) {
    // SOC开机
    pThis->soc_on_ = true;
    pThis->soc_on_count_ = 0;
  } else if (soc_state == 1) {
    // SOC关机
    pThis->soc_on_ = false;
    pThis->soc_on_count_ = 0;
    // pThis->UpdateText(QString("SOC已经关机"));
  } else {
    // SOC升级
  }
  return 0;
}

int CThreadRadarTest::OnPowerOn(QLogger &logger) {
  // 启动恢复到空闲状态
  RadarDet_SetAlgResult(8, 0);
  RadarDet_ShutSocPower();
  logger.log(QString("返回算法结果: 是否有车: 0, 停车状态: 8, 是否有车牌: 0"));
  logger.log(QString("关机"));
  return 0;
}

int CThreadRadarTest::AddAlgResult(uint32_t id, uint8_t type) {
  CTestReport::EventInfo_t info = {0};
  info.id = id;
  // info.plate_num = mark_info_.plate_num;
  info.type = type;
  alg_resl_.append(info);
  return 0;
}

int CThreadRadarTest::OnParkIn(QLogger &logger, bool has_car, bool car_change) {
  uint8_t park_state = 0;
  uint8_t has_plate = 0;
  if (has_car) {
    park_state = 1;
    has_plate = 1;
    park_out_ = false;
  } else {
    park_state = 8;
    has_plate = 0;
  }
  RadarDet_SetAlgResult(park_state, has_plate);
  AddAlgResult(trig_index_, 0);
  logger.log(
      QString("[返回算法结果: 是否有车: %1, 停车状态: %2, 是否有车牌: %3, "
              "是否换车: %4]")
          .arg(has_car)
          .arg(park_state)
          .arg(has_plate)
          .arg(car_change));
  logger.log(QString("关机"));
  RadarDet_ShutSocPower();
  return 0;
}

int CThreadRadarTest::OnParkStable(QLogger &logger, bool has_car,
                                   bool car_change) {
  uint8_t park_state = 0;
  uint8_t has_plate = 0;
  if (has_car) {
    park_state = 2;
    has_plate = 1;
    logger.log(QString("返回算法结果: 是否有车: %1, 停车状态: %2, "
                       "是否有车牌: %3, 是否换车: %4")
                   .arg(has_car)
                   .arg(park_state)
                   .arg(has_plate)
                   .arg(car_change));
  } else {
    park_state = 8;
    has_plate = 0;
  }
  RadarDet_SetAlgResult(park_state, has_plate);
  logger.log(QString("关机"));
  RadarDet_ShutSocPower();
  return 0;
}

int CThreadRadarTest::OnParkOut(QLogger &logger, bool has_car,
                                bool car_change) {
  uint8_t park_state = 0;
  uint8_t has_plate = 0;
  if (has_car) {
    if (car_change) {
      park_state = 1;
      has_plate = 1;
      park_out_ = false;
      logger.log(QString("[返回算法结果: 是否有车: %1, 停车状态: %2, "
                         "是否有车牌: %3,  是否换车: %4]")
                     .arg(has_car)
                     .arg(park_state)
                     .arg(has_plate)
                     .arg(car_change));
      AddAlgResult(trig_index_, 0);
    } else {
      park_state = 4;
      has_plate = 1;
      park_out_ = true;
      logger.log(QString("[返回算法结果: 是否有车: %1, 停车状态: %2, "
                         "是否有车牌: %3,  是否换车: %4]")
                     .arg(has_car)
                     .arg(park_state)
                     .arg(has_plate)
                     .arg(car_change));
      AddAlgResult(trig_index_, 1);
    }
  } else {
    park_state = 8;
    has_plate = 0;
    logger.log(QString("[返回算法结果: 是否有车: %1, 停车状态: %2, "
                       "是否有车牌: %3,  是否换车: %4]")
                   .arg(has_car)
                   .arg(park_state)
                   .arg(has_plate)
                   .arg(car_change));
    AddAlgResult(trig_index_, 1);
  }
  RadarDet_SetAlgResult(park_state, has_plate);
  logger.log(QString("关机"));
  RadarDet_ShutSocPower();
  return 0;
}

int CThreadRadarTest::OnParkFree(QLogger &logger, bool has_car,
                                 bool car_change) {
  uint8_t park_state = 0;
  uint8_t has_plate = 0;
  if (!has_car) {
    park_state = 8;
    has_plate = 0;
  } else {
    if (car_change) {
      park_state = 1;
      has_plate = 1;
      logger.log(QString("[返回算法结果: 是否有车: %1, 停车状态: %2, "
                         "是否有车牌: %3,  是否换车: %4]")
                     .arg(has_car)
                     .arg(park_state)
                     .arg(has_plate)
                     .arg(car_change));
      AddAlgResult(trig_index_, 1);
    } else {
      park_state = 4;
      has_plate = 1;
      logger.log(QString("[返回算法结果: 是否有车: %1, 停车状态: %2, "
                         "是否有车牌: %3,  是否换车: %4]")
                     .arg(has_car)
                     .arg(park_state)
                     .arg(has_plate)
                     .arg(car_change));
    }
  }
  RadarDet_SetAlgResult(park_state, has_plate);
  logger.log(QString("关机"));
  RadarDet_ShutSocPower();
  return 0;
}

int CThreadRadarTest::OnSnapShot(QLogger &logger, bool has_car) {
  RadarDet_SetAlgResult(soc_trig_info_.park_state & 0x0F, 1);
  RadarDet_ShutSocPower();
  logger.log(QString("秒图触发"));
  return 0;
}
int CThreadRadarTest::OnAlarm(QLogger &logger, bool has_car) {
  RadarDet_SetAlgResult(soc_trig_info_.park_state & 0x0F, 1);
  RadarDet_ShutSocPower();
  logger.log(QString("报警触发"));
  return 0;
}

int CThreadRadarTest::OnRadarTrig(uint16_t *dist, uint16_t *amp, void *param) {
  CThreadRadarTest *pThis = (CThreadRadarTest *)param;
  uint32_t data = 0;
  if (pThis->radar_index_ > (pThis->radar_data_.size() - 1)) {
    data = pThis->radar_data_.at(pThis->radar_data_.size() - 1);
  } else {
    data = pThis->radar_data_.at(pThis->radar_index_);
  }

  *amp = data & 0xFFFF;
  *dist = (data >> 16) & 0xFFFF;
  pThis->radar_index_++;
  return 0;
}

uint64_t CThreadRadarTest::GetRadarInterval(QString video_file,
                                            uint32_t radar_num) {
  AVMMediaInfo_t info = {0};
  CMediaFile *file_vi = new CMediaFile();
  char *ch;
  QByteArray ba = video_file.toUtf8();
  ch = ba.data();
  int ret = file_vi->Open(ch, 0);
  if (ret != 0) {
    return ret;
  }
  file_vi->GetInfo(&info);
  file_vi->Close();
  delete file_vi;
  double video_in_sec = (double)info.total_frames / 25.0;
  uint64_t radar_interval_in_ms = (uint64_t)(1000 * video_in_sec / radar_num);
  return radar_interval_in_ms;
}

void CThreadRadarTest::GetRadarData(QString video_file,
                                    QVector<uint32_t> &radar_data) {
  QString file_radar = CGenOpt::GetRadarFileName(video_file);
  SEI_MAP raw_data;
  int ret = 0;
  ret = CMediaFile::ReadRadarDataFromFile(file_radar.toUtf8().constData(),
                                          raw_data);
  if (ret != 0) {
    return;
  }
  auto it = raw_data.begin();
  radar_data.clear();
  for (; it != raw_data.end(); it++) {
    uint8_t *raw_array = (uint8_t *)it->second.data();
    uint32_t dist = (uint32_t)raw_array[4] << 8 | raw_array[3];
    uint32_t amp = (uint32_t)(raw_array[6]) << 8 | raw_array[5];
    uint32_t data = (dist << 16) + amp;
    radar_data.append(data);
  }
}

/*根据标定信息确认触发的时候是否有车*/
bool CThreadRadarTest::GetAlgResultFromMarkInfo(
    QVector<CTestReport::EventInfo_t> info, uint32_t idx) {
  bool has_car = false;
  uint32_t id_start = 0;
  for (const auto &event : info) {
    if ((idx <= event.id) && (idx > id_start)) {
      // 下一个节点如果是入场说明这段时间没车。
      has_car = (event.type != 0);
      break;
    }
    id_start = event.id;
  }
  return has_car;
}

int CThreadRadarTest::OnAlgResultProcess(QLogger &logger) {
  if (soc_on_) {
    // 获取当前触发点在标记数据中是否有车
    if (soc_on_count_ == 0) {
      RadarDet_GetTrigInfo(&soc_trig_info_);
      has_car_ = GetAlgResultFromMarkInfo(mark_info_, radar_index_);
      /*如果标记的时间大于2，说明中间出现过换车，则换车标志位使用雷达的。如果没有，则默认不换车*/
      if (mark_info_.size() <= 2) {
        soc_trig_info_.is_car_change = false;
      }
      logger.log(QString("SOC触发(雷达ID: %1): 原因: %2, 当前停车状态: %3")
                     .arg(radar_index_)
                     .arg(soc_trig_info_.trig_type)
                     .arg(soc_trig_info_.park_state & 0x0F));
      trig_index_ = radar_index_;
    } else if (soc_on_count_ >= 5) {

      if ((soc_trig_info_.trig_type & 2) != 0) {
        // 第一次上电,直接关机,配置算法模式
        OnPowerOn(logger);
      } else if ((soc_trig_info_.trig_type & 1) != 0) {
        // 停车触发
        switch (soc_trig_info_.park_state & 0x0F) {
        case 1: {
          // 驶入车辆停稳
          OnParkStable(logger, has_car_, soc_trig_info_.is_car_change);
          break;
        }
        case 2: {
          // 停稳车辆驶出
          OnParkOut(logger, has_car_, soc_trig_info_.is_car_change);
          break;
        }
        case 4: {
          // 驶出车辆空闲
          OnParkFree(logger, has_car_, soc_trig_info_.is_car_change);
          break;
        }
        case 8: {
          // 空闲车辆驶入
          OnParkIn(logger, has_car_, soc_trig_info_.is_car_change);
          break;
        }
        default: {
          RadarDet_SetAlgResult(soc_trig_info_.park_state & 0x0F, 1);
          RadarDet_ShutSocPower();
        }
        }
      } else if ((soc_trig_info_.trig_type & 512) != 0) {
        OnSnapShot(logger, has_car_);
      } else {
        OnAlarm(logger, has_car_);
      }
      soc_on_count_ = 0;
    }
    soc_on_count_++;
  }
  return 0;
}

int CThreadRadarTest::DoBeforeTest(QString file_name, CTestReport *report,
                                   QLogger &logger) {
  /*准备雷达数据*/
  radar_data_.clear();
  radar_index_ = 0;
  GetRadarData(file_name, radar_data_);
  if (radar_data_.size() == 0) {
    UpdateText(QString("雷达数据为空，跳过该文件!"));
    return -1;
  }
  logger.log(QString("测试文件: %1, 共%2个雷达数据")
                 .arg(file_name)
                 .arg(radar_data_.size()));
  /*重置初始变量*/
  soc_on_ = false;
  soc_on_count_ = 0;
  /*计算雷达数据时间间隔*/
  radar_interval_ = GetRadarInterval(file_name, radar_data_.size());
  if (radar_interval_ == 0) {
    UpdateText(QString("视频文件异常，跳过该文件!"));
    return -1;
  }
  RadarDet_SetExecInterval(radar_interval_);
  logger.log(QString("雷达数据时间间隔为%1ms").arg(radar_interval_));
  UpdateText(QString("有%1个雷达数据，开始测试...").arg(radar_data_.size()));

  /*获得文件名中的标定信息*/
  mark_info_.clear();
  int ret = report->GetMarkInfo(file_name, mark_info_);
  if (mark_info_.size() == 0) {
    UpdateText(QString("视频数据未标定！，跳过该文件"));
    return -1;
  }
  logger.log(QString("共有标定事件%1个:").arg(mark_info_.size()));
  for (const auto &mark_event : mark_info_) {
    logger.log(QString("[%1]: 雷达ID: %2, 车牌号: %3")
                   .arg(mark_event.type == 0 ? "入场" : "离场")
                   .arg(mark_event.id)
                   .arg(mark_event.plate_num));
  }
  /*清理上次算法处理结果*/
  alg_resl_.clear();
  return 0;
}

void CThreadRadarTest::run() {
  CTestReport test_report;
  int ret = 0;
  if (file_list_.size() == 0) {
    UpdateText("文件夹内没有可以测试的文件");
    UpdateProcess(100);
    return;
  }

  UpdateText(QString("共%1个文件，开始测试...").arg(file_list_.size()));
  for (int i = 0; i < file_list_.size(); i++) {
    QString file_name = file_list_.at(i);
    QString log_file = folder_output_ + "/log";
    QDir dir;
    if (!dir.exists(log_file)) {
      dir.mkpath(log_file);
    }
    log_file += "/" + CGenOpt::GetFileNameFromPath(file_name) + "_mcu.log";
    QString qlog_file = folder_output_ + "/log" + "/" +
                        CGenOpt::GetFileNameFromPath(file_name) + "_test.log";
    QString file_chart = folder_output_ + "/log" + "/" +
                         CGenOpt::GetFileNameFromPath(file_name) + ".jpg";
    QLogger logger(qlog_file);
    QByteArray ba = log_file.toLocal8Bit();
    ret = RadarDet_Init(ba.constData(), LOG_OUTPOS_FILE);
    if (ret != 0) {
      UpdateText("雷达测试初始化失败！测试失败！");
      UpdateProcess(100);
      return;
    }
    RadarDet_SetSOCTrigCallBack(OnSocTrig, this);
    RadarDet_SetRadarTrigCallBack(OnRadarTrig, this);
    UpdateProcess(i * 100 / file_list_.size());

    /*获得标记信息*/
    UpdateText(QString("开始测试文件:%1").arg(file_name));
    uint32_t time_begin = GetTickCount();
    if (DoBeforeTest(file_name, &test_report, logger) != 0) {
      continue;
    }

    do {
      OnAlgResultProcess(logger);
      RadarDet_Exec();
    } while (radar_index_ < (radar_data_.size() - 1));
    // UpdateText(QString("雷达数据测试测试完成,用时%1s")
    //                .arg((GetTickCount() - time_begin) / 1000));
    test_report.AddTestResult(file_name, alg_resl_);
    test_report.MakeChart(file_chart, radar_data_, mark_info_, alg_resl_);
    RadarDet_Uninit();
  }
  QString file_report_name =
      QString("/report%1.csv")
          .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
  QString file_report = folder_output_ + file_report_name;
  ret = test_report.MakeReport(file_report);
  if (ret == 0) {
    UpdateText(QString("生成报告文件: %1").arg(file_report));
  } else {
    UpdateText(QString("生成报告文件失败"));
  }

  UpdateProcess(100);
}
